﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Documents;
using System;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;


namespace RemoveUnUseResource
{
    /// <summary>
    /// Interaction logic for FindUndefineResource.xaml
    /// </summary>
    public partial class FindUndefineResource : Window
    {
        #region family field
        /// <summary>
        /// key: resource Key
        /// Value: filename,  if public resource, filename is null
        /// </summary>
        Dictionary<string, string> allResource = new Dictionary<string, string>();

        /// <summary>
        /// usedResource
        /// </summary>
        Dictionary<string, string> allUsedResource = new Dictionary<string, string>();

        /// <summary>
        /// key: use page name
        /// value: use resource
        /// </summary>
        Dictionary<string, List<string>> allUnDefinedResource = new Dictionary<string, List<string>>();

        /// <summary>
        /// all xaml file in this folder
        /// </summary>
        List<string> allXamlFilePaths = new List<string>();

        /// <summary>
        /// current path
        /// </summary>
        string path = string.Empty;
        #endregion

        #region constructor
        public FindUndefineResource()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }
        #endregion

        #region event
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            btnSelectPath.Click += new RoutedEventHandler(btnSelectPath_Click);
        }

        void btnSelectPath_Click(object sender, RoutedEventArgs e)
        {
            Task.Factory.StartNew(delegate { DoTask(); });
            System.Windows.Forms.FolderBrowserDialog folderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
            if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                path = folderBrowserDialog.SelectedPath;
                tbPath.Text = path;

                Task.Factory.StartNew(delegate { DoTask(); });
            }
        }
        #endregion

        #region method
        void DoTask()
        {
            if (!Directory.Exists(path)) return;

            showMessage("collect path .....");
            getPathRecursion(path);

            foreach (string xamlPath in allXamlFilePaths)
            {
                showMessage("current xaml Path:" + xamlPath);
                List<string> resource, usedkeys;
                getFromXaml(xamlPath, out resource, out usedkeys);

                if (resource != null && resource.FirstOrDefault() != null)
                {
                    var newItems = resource.Except(allResource.Keys);
                    if (newItems != null && newItems.FirstOrDefault() != null)
                    {
                        foreach (var item in newItems.ToList())
                        {
                            allResource.Add(item, xamlPath);
                        }
                    }
                }

                var newKeys = usedkeys.Except(allUsedResource.Keys);
                if (newKeys != null && newKeys.FirstOrDefault() != null)
                {
                    foreach (var newKey in newKeys)
                        allUsedResource.Add(newKey, xamlPath);
                }
            }


            checkResoruce();
            string outputPath = output();
            showMessage("output is :" + outputPath);
        }

        void showMessage(string text)
        {
            invoke(delegate { tbPath.Text = text; });
        }

        void invoke(Action a)
        {
            this.Dispatcher.Invoke(a);
        }

        void getPathRecursion(string currentPath)
        {
            var paths = Directory.GetFileSystemEntries(currentPath);
            foreach (string path in paths)
            {
                if (Directory.Exists(path))
                    getPathRecursion(path);
                else
                {
                    string extension = Path.GetExtension(path);
                    if (extension.Equals(".xaml", System.StringComparison.CurrentCultureIgnoreCase))
                    {
                        allXamlFilePaths.Add(path);
                    }
                }
            }
        }

        void getFromXaml(string path, out List<string> resourceKeys, out List<string> usedKeys)
        {
            string content = File.ReadAllText(path);
            resourceKeys = new List<string>();
            usedKeys = new List<string>();
            string matchValue = string.Empty;

            #region find resource
            Regex regexObj = new Regex("x:key=\"([^\"]+)\"", RegexOptions.Singleline | RegexOptions.IgnoreCase);

            Match matchResult = regexObj.Match(content);
            while (matchResult.Success)
            {
                matchValue = matchResult.Groups[1].Value;
                if (!matchValue.StartsWith("{") && !resourceKeys.Contains(matchValue))
                    resourceKeys.Add(matchValue);
                matchResult = matchResult.NextMatch();
            }
            #endregion

            #region find  used
            Regex regexUsed = new Regex("{DynamicResource ([^\"]+)}", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            Match contentMatchResult = regexUsed.Match(content);
            while (contentMatchResult.Success)
            {
                matchValue = contentMatchResult.Groups[1].Value;
                if (!matchValue.StartsWith("{") && !usedKeys.Contains(matchValue))
                    usedKeys.Add(matchValue);
                contentMatchResult = contentMatchResult.NextMatch();
            }

            regexUsed = new Regex("{StaticResource ([^\"]+)}", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            contentMatchResult = regexUsed.Match(content);
            while (contentMatchResult.Success)
            {
                matchValue = contentMatchResult.Groups[1].Value;
                if (!matchValue.Contains("{") && !usedKeys.Contains(matchValue) && !matchValue.Contains("}"))
                    usedKeys.Add(matchValue);
                contentMatchResult = contentMatchResult.NextMatch();
            }

            regexUsed = new Regex("{StaticResource ([^\"]+)}}", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            contentMatchResult = regexUsed.Match(content);
            while (contentMatchResult.Success)
            {
                matchValue = contentMatchResult.Groups[1].Value;
                if (!matchValue.StartsWith("{") && !usedKeys.Contains(matchValue))
                    usedKeys.Add(matchValue);
                contentMatchResult = contentMatchResult.NextMatch();
            }
            #endregion
        }

        void addUnDefinedResource(string path, string resourceKey)
        {
            if (allUnDefinedResource.Keys.Contains(path))
            {
                var resources = allUnDefinedResource[path];
                if (resources == null)
                    resources = new List<string> { resourceKey };
                else
                    resources.Add(resourceKey);
            }
            else
            {
                allUnDefinedResource.Add(path, new List<string> { resourceKey });
            }
        }

        void checkResoruce()
        {
            foreach (string key in allUsedResource.Keys)
            {
                if (allResource.ContainsKey(key))
                    continue;
                else
                {
                    addUnDefinedResource(allUsedResource[key], key);
                }
            }
        }

        string output()
        {
            StringBuilder builder = new StringBuilder();

            foreach (string filePath in allUnDefinedResource.Keys)
            {
                builder.AppendLine(filePath);
                var removeResource = allUnDefinedResource[filePath];
                foreach (string resource in removeResource)
                {
                    builder.AppendLine("     " + resource);
                }
            }
            string path = DateTime.Now.ToString("hhmmssfff_") + @"UndefineResource.txt";
            File.WriteAllText(path, builder.ToString());
            Process.Start(path);
            return path;
        }
        #endregion
    }
}
